// @Title        passwd.go
// @Description  密码读取与计算
package main

import (
	"crypto/rand"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/base64"
	"strings"
)

// 密码加密算法类型常量
type HashType string

const (
	// HashTypeYUANQI YUANQI兼容模式，使用SHA1和UTF-16编码
	HashTypeYUANQI HashType = "YUANQI"
	// HashTypeSHA256 SHA256加密模式，默认使用
	HashTypeSHA256 HashType = "SHA256"
	// HashTypeSHA1 SHA1加密模式
	HashTypeSHA1 HashType = "SHA1"
	// HashTypeSHA512 SHA512加密模式
	HashTypeSHA512 HashType = "SHA512"

	HashTypeNONE HashType = ""
)

func HashTypeFromStr(str string) HashType {
	switch str {
	case "YUANQI":
		return HashTypeYUANQI
	case "SHA256":
		return HashTypeSHA256
	case "SHA1":
		return HashTypeSHA1
	case "SHA512":
		return HashTypeSHA512
	default:
		return HashTypeNONE
	}
}

// @title        Salt
// @description  生成一个特定字节长度的盐字符串
// @param        {int} length - 字节长度（不是字符串的长度，字符串是base64转出来的），不写则用默认值
// @return       {string} 盐字符串
// 保留函数签名但添加nolint标记，避免静态检查误报
//
//nolint:unused
func to_salt(hashType HashType) (string, error) {
	length := 16
	switch hashType {
	case HashTypeYUANQI:
		length = sha1.Size
	case HashTypeSHA256:
		length = sha256.Size
	case HashTypeSHA1:
		length = sha1.Size
	case HashTypeSHA512:
		length = sha512.Size
	default:
		length = 32
	}

	b := make([]byte, length)
	_, err := rand.Read(b)
	if err != nil {
		return "", err
	}
	return base64.URLEncoding.EncodeToString(b), nil
}

// @title        PasswdEncrypt
// @description  已知密码的明文，返回其Hash串。格式：[协议名] + [hash] + [salt]
// @param        {string} passwd - 明文密码
// @param        {HashType} protocol - 协议类型
// @return       {string} 可以存储在数据库中的Hash串
func passwdHash(passwd string, saltType HashType) (string, error) {
	salt, err := to_salt(saltType)
	if err != nil {
		return "", err
	}

	hashed, err := hashBase64(passwd, salt, saltType)
	if err != nil {
		return "", err
	}

	return "{" + string(saltType) + "}" + hashed + salt, nil
}

// @title        HashBase64
// @description  先执行哈希函数，然后转为base64格式
// @param        {string} str - 需要转换的字符串
// @param        {string} hashType - 哈希函数类型
// @param        {bool} useStandard - 是否使用标准base64编码（默认为false，使用URL编码）
// @return       {string} 执行hash然后base64的结果（只支持SHA1和SHA256算法）
func hashBase64(passwd string, salt string, hashType HashType) (string, error) {
	var b []byte
	var result string
	switch hashType {
	case HashTypeSHA256:
		h := sha256.New()
		h.Write([]byte(passwd + salt))
		b = h.Sum(nil)
		result = base64.URLEncoding.EncodeToString(b)
	case HashTypeSHA1:
		h := sha1.New()
		h.Write([]byte(passwd + salt))
		b = h.Sum(nil)
		result = base64.URLEncoding.EncodeToString(b)
	case HashTypeYUANQI:
		hashBytes, err := passwdEncryptYUANQI(passwd, salt)
		if err != nil {
			return "", err
		}
		b = hashBytes
		result = base64.StdEncoding.EncodeToString(b)
	case HashTypeSHA512:
		h := sha512.New()
		h.Write([]byte(passwd + salt))
		b = h.Sum(nil)
		result = base64.URLEncoding.EncodeToString(b)
	default:
		return passwd, nil
	}

	return result, nil
}

// @title        passwdEncryptYUANQI 远齐C#兼容模式
// @description  对密码进行SHA1加密（支持UTF-16编码，兼容C#生成的数据）
// @param        {string} passwd - 明文密码
// @param        {string} salt - 盐值（可选，可能是base64编码）
// @return       {string} SHA1加密后的哈希值（base64编码）
func passwdEncryptYUANQI(passwd string, salt string) ([]byte, error) {
	// 尝试解码base64编码的盐值
	var saltBytes []byte
	var err error

	// 首先尝试将salt作为base64解码
	saltBytes, err = base64.StdEncoding.DecodeString(salt)
	if err != nil {
		// 如果解码失败，直接使用原始字符串作为盐值
		// 这是为了兼容从哈希字符串中直接提取的非base64编码的盐值
		saltBytes = []byte(salt)
	}

	// 使用UTF-16小端字节序编码密码（兼容C#的Unicode.GetBytes）
	passBytes := make([]byte, 0, len(passwd)*2)
	for _, c := range passwd {
		passBytes = append(passBytes, byte(c&0xFF))
		passBytes = append(passBytes, byte(c>>8))
	}

	// 计算SHA1(盐+密码) - 注意这里是盐在前，密码在后
	hasher := sha1.New()
	hasher.Write(saltBytes)
	hasher.Write(passBytes)
	hashBytes := hasher.Sum(nil)

	// 返回base64编码的哈希值
	return hashBytes, nil
}

// @title        PasswdSalt
// @description  切出hash串的盐部分
// @param        {string} passwdHash - [协议名] + [hash] + [salt] 格式的hash串
// @return       {HashType, string, string, error} 协议类型,hash串的hash,hash串的盐
func passwdto_salt(passwdHash string) (HashType, string, string, error) {
	switch {
	case strings.HasPrefix(passwdHash, "{"+string(HashTypeSHA256)+"}"):
		hashed := passwdHash[8:52]
		salt := passwdHash[52:]
		return HashTypeSHA256, hashed, salt, nil
	case strings.HasPrefix(passwdHash, "{"+string(HashTypeSHA1)+"}"):
		hashed := passwdHash[6:34]
		salt := passwdHash[34:]
		return HashTypeSHA1, hashed, salt, nil
	case strings.HasPrefix(passwdHash, "{"+string(HashTypeYUANQI)+"}"):
		hashed := passwdHash[8:36]
		salt := passwdHash[36:]
		return HashTypeYUANQI, hashed, salt, nil
	case strings.HasPrefix(passwdHash, "{"+string(HashTypeSHA512)+"}"):
		hashed := passwdHash[8:96]
		salt := passwdHash[96:]
		return HashTypeSHA512, hashed, salt, nil
	default:
		return HashTypeNONE, passwdHash, "", nil
	}
}

// @title        PasswdCheck
// @description  更新密码验证函数，支持多种加密格式
// @param        {string} passwdHash - 密码哈希字符串
// @param        {string} passwd - 待验证的明文密码
// @return       {bool} 验证结果
func PasswdCheck(passwdHash string, passwd string) bool {
	// 去除字符串两端的空白字符
	passwdHash = strings.TrimSpace(passwdHash)
	passwd = strings.TrimSpace(passwd)

	passType, hashed, salt, err := passwdto_salt(passwdHash)
	if err != nil {
		return false
	}

	hashed2, err := hashBase64(passwd, salt, passType)
	if err != nil {
		return false
	}

	return hashed2 == hashed
}

func passwdCheckWithto_salt(passwdHash string, passwd string, sessionSalt string) bool {
	// 去除字符串两端的空白字符
	passwdHash = strings.TrimSpace(passwdHash)
	passwd = strings.TrimSpace(passwd)

	passType, hashed, _, err := passwdto_salt(passwdHash)
	if err != nil {
		return false
	}

	passwd2, err := hashBase64(hashed, sessionSalt, passType)
	if err != nil {
		return false
	}
	return passwd2 == passwd
}
